import asyncio
import ssl
import filesolver
import logging
import json
import struct


HEADERSIZE = 12
REQUEST, RESPONSE = range(2)
VER = 1

logger = logging.getLogger('server')
logging.basicConfig(level=logging.DEBUG)


class AiosslServerProtocol(asyncio.Protocol):
    def __init__(self):
        self.transport = None
        self._data_buffer = bytes()

    def connection_made(self, transport):
        peername = transport.get_extra_info('peername')
        logger.debug('Connection from {}'.format(peername))
        self.transport = transport

    def data_received(self, data):
        asyncio.ensure_future(self._solve_received(data))

    async def _solve_received(self, data):
        self._data_buffer += data
        if len(self._data_buffer) < 12:
            return
        head_pack = struct.unpack('!3I', self._data_buffer[:HEADERSIZE])
        body_size = head_pack[1]
        if len(self._data_buffer) < HEADERSIZE + body_size:
            return
        body = self._data_buffer[HEADERSIZE:HEADERSIZE + body_size]
        self._data_buffer = self._data_buffer[HEADERSIZE + body_size:]

        content = await self._construct_response(body)
        if content:
            content = content.encode()
            head_pack = struct.pack("!3I", *[VER, len(content), RESPONSE])
            message = head_pack + content
            self.transport.write(message)
        else:
            logger.warning('Receive doubtful message')

    async def _construct_response(self, body):
        message = json.loads(body.decode())
        logger.debug('Receive message: {!r}'.format(message))
        if message['op'] == 'getblock':
            content = await filesolver.read_file(message['file'], message['offset'], message['size'])
            if content:
                result = json.dumps({'op': 'getblock', 'file': message['file'], 'offset': message['offset'], 'size': message['size'], 'content': content, 'statuscode': 0})
            else:
                result = json.dumps({'statuscode': -1})
        elif message['op'] == 'metadata':
            content = await filesolver.get_metadata(message['file'])
            if content:
                result = json.dumps({'op': 'metadata', 'file': message['file'], 'offset': message['offset'], 'size': message['size'], 'content': content, 'statuscode': 0})
            else:
                result = json.dumps({'statuscode': -1})
        else:
            return False
        return result

        # logger.debug('Close the client socket')
        # self.transport.close()


class Server:
    def __init__(self):
        self.sock = None

    def sslins(self):
        sslcontext = ssl.SSLContext(ssl.PROTOCOL_SSLv23)
        sslcontext.load_cert_chain(certfile="cert.pem", keyfile="key.pem")
        return sslcontext

    def listen(self, ip=None, port=0):
        loop = asyncio.get_event_loop()
        listen = loop.create_server(AiosslServerProtocol, ip, port, ssl=self.sslins())
        self.sock = loop.run_until_complete(listen)

    def close(self):
        self.sock.close()


if __name__ == '__main__':
    loop = asyncio.get_event_loop()
    server = Server()
    server.listen('127.0.0.1', 8888)
    server2 = Server()
    server2.listen('127.0.0.1', 8889)

    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass

    server.close()
    loop.close()
